home *** CD-ROM | disk | FTP | other *** search
- unit GifCode;
- {Freeware GIF image component}
-
- {Based on GifUtl.pas (c)1993 Sean Wenzel Compuserve 71736,1245}
-
- {Converted to Delphi by Richard Dominelli RichardA_Dominelli@mskcc.org
- dopey@felix.mskcc.org
- Compuserve 73541,2555}
- {Converted to Delphi 2 and made into an
- image component by Richard Shotbolt Compuserve 100327,2305
- }
-
- interface
-
- uses WinTypes,
- Forms,
- SysUtils,
- Classes,
- ExtCtrls;
-
- const
- { image descriptor bit masks }
- idLocalColorTable = $80; { set if a local color table follows }
- idInterlaced = $40; { set if image is interlaced }
- idSort = $20; { set if color table is sorted }
- idReserved = $0C; { reserved - must be set to $00 }
- idColorTableSize = $07; { size of color table as above }
- Trailer: byte = $3B; { indicates the end of the GIF data stream }
- ExtensionIntroducer: byte = $21;
- MAXSCREENWIDTH = 800;
- ImageSeparator: byte = $2C;
- { logical screen descriptor packed field masks }
- lsdGlobalColorTable = $80; { set if global color table follows L.S.D. }
- lsdColorResolution = $70; { Color resolution - 3 bits }
- lsdSort = $08; { set if global color table is sorted - 1 bit }
- lsdColorTableSize = $07; { size of global color table - 3 bits }
- { Actual size = 2^value+1 - value is 3 bits }
- BlockTerminator: byte = 0; { terminates stream of data blocks }
- MAXCODES = 4095; { the maximum number of different codes 0 inclusive }
- { error constants }
- geNoError = 0; { no errors found }
- geNoFile = 1; { gif file not found }
- geNotGIF = 2; { file is not a gif file }
- geNoGlobalColor = 3; { no Global Color table found }
- geImagePreceded = 4; { image descriptor preceeded by other unknown data }
- geEmptyBlock = 5; { Block has no data }
- geUnExpectedEOF = 6; { unexpected EOF }
- geBadCodeSize = 7; { bad code size }
- geBadCode = 8; { Bad code was found }
- geBitSizeOverflow = 9; { bit size went beyond 12 bits }
- geNoBMP = 10; { Could not make BMP file }
-
- ErrName: Array[1..10] of string = (
- 'GIF file not found',
- 'Not a GIF file',
- 'Missing color table',
- 'Bad data',
- 'No data',
- 'Unexpected EOF',
- 'Bad code size',
- 'Bad code',
- 'Bad bit size',
- 'Bad bitmap');
-
- CodeMask: array[0..12] of integer = ( { bit masks for use with Next code }
- 0,
- $0001, $0003,
- $0007, $000F,
- $001F, $003F,
- $007F, $00FF,
- $01FF, $03FF,
- $07FF, $0FFF);
-
- type
- TDataSubBlock = record
- Size: byte; { size of the block -- 0 to 255 }
- Data: array[1..255] of byte; { the data }
- end;
-
- type
- THeader = record
- Signature: array[0..2] of char; { contains 'GIF' }
- Version: array[0..2] of char; { '87a' or '89a' }
- end;
-
- TLogicalScreenDescriptor = record
- ScreenWidth: word; { logical screen width }
- ScreenHeight: word; { logical screen height }
- PackedFields: byte; { packed fields - see below }
- BackGroundColorIndex: byte; { index to global color table }
- AspectRatio: byte; { actual ratio = (AspectRatio + 15) / 64 }
- end;
-
- type
- TColorItem = record { one item a a color table }
- Red: byte;
- Green: byte;
- Blue: byte;
- end;
-
- TColorTable = array[0..255] of TColorItem; { the color table }
-
- type
- TImageDescriptor = record
- Separator: byte; { fixed value of ImageSeparator }
- ImageLeftPos: word; { Column in pixels in respect to left edge of logical screen }
- ImageTopPos: word; { row in pixels in respect to top of logical screen }
- ImageWidth: word; { width of image in pixels }
- ImageHeight: word; { height of image in pixels }
- PackedFields: byte; { see below }
- end;
-
- { other extension blocks not currently supported by this unit
- - Graphic Control extension
- - Comment extension I'm not sure what will happen if these blocks
- - Plain text extension are encountered but it'll be interesting
- - application extension }
-
- type
- TExtensionBlock = record
- Introducer: byte; { fixed value of ExtensionIntroducer }
- ExtensionLabel: byte;
- BlockSize: byte;
- end;
-
- PCodeItem = ^TCodeItem;
-
- TCodeItem = record
- Code1, Code2: byte;
- end;
- {===============================================================}
- { Bitmap File Structs
- {===============================================================}
-
- type
- GraphicLine = array [0..2048] of byte;
- PBmLine = ^TBmpLinesStruct;
- TBmpLinesStruct = record
- LineData : GraphicLine;
- LineNo : Integer;
- end;
- {------------------------------------------------------------------------------}
-
- type
- { This is the actual gif object }
- PGif = ^TGif;
- TGif = class(TObject)
- private
- GifStream: TMemoryStream; { the file stream for the gif file }
- Header: THeader; { gif file header }
- LogicalScreen: TLogicalScreenDescriptor; { gif screen descriptor }
- GlobalColorTable: TColorTable; { global color table }
- LocalColorTable: TColorTable; { local color table }
- ImageDescriptor: TImageDescriptor; { image descriptor }
- UseLocalColors: boolean; { true if local colors in use }
- Interlaced: boolean; { true if image is interlaced }
- LZWCodeSize: byte; { minimum size of the LZW codes in bits }
- ImageData: TDataSubBlock; { variable to store incoming gif data }
- TableSize: word; { number of entrys in the color table }
- BitsLeft, BytesLeft: integer; { bits left in byte - bytes left in block }
- BadCodeCount: word; { bad code counter }
- CurrCodeSize: integer; { Current size of code in bits }
- ClearCode: integer; { Clear code value }
- EndingCode: integer; { ending code value }
- Slot: word; { position that the next new code is to be added }
- TopSlot: word; { highest slot position for the current code size }
- HighCode: word; { highest code that does not require decoding }
- NextByte: integer; { the index to the next byte in the datablock array }
- CurrByte: byte; { the current byte }
- DecodeStack: array[0..MAXCODES] of byte; { stack for the decoded codes }
- Prefix: array[0..MAXCODES] of integer; { array for code prefixes }
- Suffix: array[0..MAXCODES] of integer; { array for code suffixes }
- LineBuffer: GraphicLine; { array for buffer line output }
- CurrentX, CurrentY: integer; { current screen locations }
- Status: word;
- InterlacePass: byte; { interlace pass number }
- {Conversion Routine Vars}
- BmHeader : TBitmapInfoHeader; {File Header for bitmap file}
- ImageLines: TList; {Image data}
- BmpStream: TMemoryStream;
- {Member Functions}
- procedure ParseMem;
- function NextCode: word; { returns the next available code }
- procedure Error(ErrCode: integer);
- procedure InitCompressionStream; { initializes info for decode }
- procedure ReadSubBlock; { reads a data subblock from the stream }
- procedure CreateLine;
- procedure CreateBitHeader; {Takes the gif header information and converts it to BMP}
- public
- constructor Create;
- destructor Destroy; override;
- procedure Decode;
- procedure GifToBmp(AGifName, ABmpName: string);
- procedure GifConvert(AGifName: string);
- procedure ConvertfromMem(AMemStream:TMemoryStream;ABmpName:string);
- procedure WriteBitmapToStream;
- procedure WriteBitmapToFile(ABMPName: string); {Writes out the header info
- writes out the pallet in correct order.
- Arranges the lines in correct order.
- Writes out the image lines in correct order}
-
- end;
-
- type
- TGifImage = class(TImage)
- private
- FGifFileName: string;
- IGif: TGif;
- procedure SetGifFileName(Value: string);
- public
- constructor Create(AOwner:TComponent); override;
- destructor Destroy; override;
- published
- property GifFileName: string read FGifFileName write SetGifFileName;
- end;
-
- type
- EGifException = class(Exception)
- end;
-
- procedure Register;
-
- implementation
-
- function Power(A, N: real): real; { returns A raised to the power of N }
- begin
- Power := exp(N * ln(A));
- end;
-
- {------------------------------------------------------------------------------}
- { TGifImage }
-
- constructor TGifImage.Create(AOwner:TComponent);
- begin
- IGif := TGif.Create;
- inherited Create(AOwner);
- end;
-
- destructor TGifImage.Destroy;
- begin
- IGif.Free;
- inherited Destroy;
- end;
-
- procedure TGifImage.SetGifFileName(Value: string);
- {Loads the GIF file into the image}
- {If you don't like the delay turn the hourglass on}
- begin
- try
- Picture.Bitmap := nil; { Clear the image }
- IGif.GifConvert(Value);{ Convert GIF file to in-memory bitmap }
- Picture.Bitmap.LoadFromStream(IGif.BmpStream); { Load new BMP from memory }
- if Visible then { Force a repaint (avoids flecks etc }
- Paint;
- FGifFileName := UpperCase(Value);
- except
- on Exception do
- begin
- Picture.Bitmap := nil; { No picture }
- IGif.Free; { Free then recreate TGif }
- IGif := TGif.Create;
- Beep;
- FGifFileName := '';
- end;
- end;
- end;
-
- {------------------------------------------------------------------------------}
-
- { TGif }
- constructor TGif.Create;
- begin
- {Create Memory Buffer to hold gif}
- GifStream := TMemoryStream.Create;
- BmpStream := TMemoryStream.Create;
- ImageLines := TList.Create;
- end;
- {------------------------------------------------------------------------------}
-
- destructor TGif.Destroy;
- begin
- GifStream.Free;
- BmpStream.Free;
- ImageLines.Free;
- inherited Destroy;
- end;
- {------------------------------------------------------------------------------}
-
- procedure TGif.GifToBmp(AGifName, ABmpName: string);
- {Convert GIF file to BMP file}
- begin
- GifConvert(AGifName);
- BmpStream.SaveToFile(ABMPName);
- end;
- {------------------------------------------------------------------------------}
-
- procedure TGif.GifConvert(AGifName: string);
- begin
- { Converts GIF file to bitstream }
- GifStream.LoadFromFile(AGifName); { Load the file into memory }
- ParseMem;
- { Create the bitmap header info }
- CreateBitHeader;
- { Decode the GIF }
- Decode;
- WriteBitmapToStream;
- end;
- {------------------------------------------------------------------------------}
-
- procedure TGif.ConvertfromMem(AMemStream: TMemoryStream; ABmpName: string);
- begin
- GifStream.LoadFromStream(AMemStream);
- GifConvert(ABmpName);
- end;
- {------------------------------------------------------------------------------}
-
- {Raise exception with a message}
- procedure TGif.Error(ErrCode: integer);
- begin
- Raise EGifException.Create(ErrName[ErrCode]);
- end;
- {------------------------------------------------------------------------------}
-
- procedure TGif.ParseMem;
- {Decodes the header and palette info}
- begin
- GifStream.Read(Header, sizeof(Header)); { read the header }
- {Stupid validation tricks}
- if Header.Signature <> 'GIF' then
- Error(geNotGif); { is vaild signature }
- {Decode the header information}
- GifStream.Read(LogicalScreen, sizeof(LogicalScreen));
- if LogicalScreen.PackedFields and lsdGlobalColorTable = lsdGlobalColorTable then
- begin
- TableSize := Trunc(Power(2,(LogicalScreen.PackedFields and lsdColorTableSize)+1));
- GifStream.Read(GlobalColorTable, TableSize*sizeof(TColorItem)); { read Global Color Table }
- end
- else
- Error(geNoGlobalColor);
- {Done with Global Headers}
- {Image specific headers}
- GifStream.Read(ImageDescriptor, sizeof(ImageDescriptor)); { read image descriptor }
- {Decode image header info}
- if ImageDescriptor.Separator <> ImageSeparator then { verify that it is the descriptor }
- Error(geImagePreceded);
- {Check for local color table}
- if ImageDescriptor.PackedFields and idLocalColorTable = idLocalColorTable then
- begin { if local color table }
- TableSize := Trunc(Power(2,(ImageDescriptor.PackedFields and idColorTableSize)+1));
- GifStream.Read(LocalColorTable, TableSize*sizeof(TColorItem)); { read Local Color Table }
- UseLocalColors := True;
- end
- else
- UseLocalColors := False;
- {Check for interlaced}
- if ImageDescriptor.PackedFields and idInterlaced = idInterlaced then
- begin
- Interlaced := true;
- InterlacePass := 0;
- end;
- {End of image header stuff}
- {Reset then Expand capacity of the Image Lines list}
- ImageLines.Clear;
- ImageLines.Capacity := ImageDescriptor.ImageHeight;
- if (GifStream = nil) then { check for stream error }
- Error(geNoFile);
- end;
- {------------------------------------------------------------------------------}
-
- procedure TGif.InitCompressionStream;
- begin
- {InitGraphics;} { Initialize the graphics display }
- GifStream.Read(LZWCodeSize, sizeof(byte)); { get minimum code size }
- if not (LZWCodeSize in [2..9]) then { valid code sizes 2-9 bits }
- Error(geBadCodeSize);
- CurrCodeSize := succ(LZWCodeSize); { set the initial code size }
- ClearCode := 1 shl LZWCodeSize; { set the clear code }
- EndingCode := succ(ClearCode); { set the ending code }
- HighCode := pred(ClearCode); { set the highest code not needing decoding }
- BytesLeft := 0; { clear other variables }
- BitsLeft := 0;
- CurrentX := 0;
- CurrentY := 0;
- end;
- {------------------------------------------------------------------------------}
-
- procedure TGif.ReadSubBlock;
- begin
- GifStream.Read(ImageData.Size, sizeof(ImageData.Size)); { get the data block size }
- if ImageData.Size = 0 then
- Error(geEmptyBlock); { check for empty block }
- GifStream.Read(ImageData.Data, ImageData.Size); { read in the block }
- NextByte := 1; { reset next byte }
- BytesLeft := ImageData.Size; { reset bytes left }
- end;
- {------------------------------------------------------------------------------}
-
- function TGif.NextCode: word; { returns a code of the proper bit size }
- begin
- if BitsLeft = 0 then { any bits left in byte ? }
- begin { any bytes left }
- if BytesLeft <= 0 then { if not get another block }
- ReadSubBlock;
- CurrByte := ImageData.Data[NextByte]; { get a byte }
- inc(NextByte); { set the next byte index }
- BitsLeft := 8; { set bits left in the byte }
- dec(BytesLeft); { decrement the bytes left counter }
- end;
- Result := CurrByte shr (8 - BitsLeft); { shift off any previosly used bits}
- while CurrCodeSize > BitsLeft do { need more bits ? }
- begin
- if BytesLeft <= 0 then { any bytes left in block ? }
- ReadSubBlock; { if not read in another block }
- CurrByte := ImageData.Data[NextByte]; { get another byte }
- inc(NextByte); { increment NextByte counter }
- Result := Result or (CurrByte shl BitsLeft); { add the remaining bits to the return value }
- BitsLeft := BitsLeft + 8; { set bit counter }
- Dec(BytesLeft); { decrement bytesleft counter }
- end;
- BitsLeft := BitsLeft - CurrCodeSize; { subtract the code size from bitsleft }
- Result := Result and CodeMask[CurrCodeSize];{ mask off the right number of bits }
- end;
-
- {------------------------------------------------------------------------------}
-
- procedure TGif.Decode;
- { this procedure actually decodes the GIF image }
- var
- SP: integer; { index to the decode stack }
-
- { local procedure that decodes a code and puts it on the decode stack }
- procedure DecodeCode(var Code: word);
- begin
- while Code > HighCode do { rip thru the prefix list placing suffixes }
- begin { onto the decode stack }
- DecodeStack[SP] := Suffix[Code]; { put the suffix on the decode stack }
- inc(SP); { increment decode stack index }
- Code := Prefix[Code]; { get the new prefix }
- end;
- DecodeStack[SP] := Code; { put the last code onto the decode stack }
- inc(SP); { increment the decode stack index }
- end;
-
- var
- TempOldCode, OldCode: word;
- BufCnt: word; { line buffer counter }
- Code, C: word;
- CurrBuf: word; { line buffer index }
- MaxVal: boolean;
- begin
- InitCompressionStream; { Initialize decoding paramaters }
- OldCode := 0;
- SP := 0;
- BufCnt := ImageDescriptor.ImageWidth; { set the Image Width }
- CurrBuf := 0;
- MaxVal := False;
- C := NextCode; { get the initial code - should be a clear code }
- while C <> EndingCode do { main loop until ending code is found }
- begin
- if C = ClearCode then { code is a clear code - so clear }
- begin
- CurrCodeSize := LZWCodeSize + 1; { reset the code size }
- Slot := EndingCode + 1; { set slot for next new code }
- TopSlot := 1 shl CurrCodeSize; { set max slot number }
- while C = ClearCode do
- C := NextCode; { read until all clear codes gone - shouldn't happen }
- if C = EndingCode then
- Error(geBadCode); { ending code after a clear code }
- if C >= Slot then { if the code is beyond preset codes then set to zero }
- C := 0;
- OldCode := C;
- DecodeStack[sp] := C; { output code to decoded stack }
- inc(SP); { increment decode stack index }
- end
- else { the code is not a clear code or an ending code so it must }
- begin { be a code code - so decode the code }
- Code := C;
- if Code < Slot then { is the code in the table? }
- begin
- DecodeCode(Code); { decode the code }
- if Slot <= TopSlot then
- begin { add the new code to the table }
- Suffix[Slot] := Code; { make the suffix }
- PreFix[slot] := OldCode; { the previous code - a link to the data }
- inc(Slot); { increment slot number }
- OldCode := C; { set oldcode }
- end;
- if Slot >= TopSlot then { have reached the top slot for bit size }
- begin { increment code bit size }
- if CurrCodeSize < 12 then { new bit size not too big? }
- begin
- TopSlot := TopSlot shl 1; { new top slot }
- inc(CurrCodeSize) { new code size }
- end
- else
- MaxVal := True; { Must check next code is a start code }
- end;
- end
- else
- begin { the code is not in the table }
- if Code <> Slot then
- Error(geBadCode); { so error out }
- { the code does not exist so make a new entry in the code table
- and then translate the new code }
- TempOldCode := OldCode; { make a copy of the old code }
- while OldCode > HighCode do { translate the old code and place it }
- begin { on the decode stack }
- DecodeStack[SP] := Suffix[OldCode]; { do the suffix }
- OldCode := Prefix[OldCode]; { get next prefix }
- end;
- DecodeStack[SP] := OldCode; { put the code onto the decode stack }
- { but DO NOT increment stack index }
- { the decode stack is not incremented because because we are only
- translating the oldcode to get the first character }
- if Slot <= TopSlot then
- begin { make new code entry }
- Suffix[Slot] := OldCode; { first char of old code }
- Prefix[Slot] := TempOldCode; { link to the old code prefix }
- inc(Slot); { increment slot }
- end;
- if Slot >= TopSlot then { slot is too big }
- begin { increment code size }
- if CurrCodeSize < 12 then
- begin
- TopSlot := TopSlot shl 1; { new top slot }
- inc(CurrCodeSize); { new code size }
- end
- else
- MaxVal := True; { Must check next code is a start code }
- end;
- DecodeCode(Code); { now that the table entry exists decode it }
- OldCode := C; { set the new old code }
- end;
- end;
- { the decoded string is on the decode stack so pop it off and put it
- into the line buffer }
- while SP > 0 do
- begin
- dec(SP);
- LineBuffer[CurrBuf] := DecodeStack[SP];
- inc(CurrBuf);
- dec(BufCnt);
- if BufCnt = 0 then { is the line full ? }
- begin
- CreateLine;
- CurrBuf := 0;
- BufCnt := ImageDescriptor.ImageWidth;
- end;
- end;
- C := NextCode; { get the next code and go at is some more }
- if (MaxVal = True) and (C <> ClearCode) then
- Error(geBitSizeOverflow);
- MaxVal := False;
- end;
- end;
- {------------------------------------------------------------------------------}
-
- procedure TGif.CreateBitHeader;
- { This routine takes the values from the GIF image
- descriptor and fills in the appropriate values in the
- bit map header struct. }
- begin
- BmHeader.biSize := Sizeof(TBitmapInfoHeader);
- BmHeader.biWidth := ImageDescriptor.ImageWidth;
- BmHeader.biHeight := ImageDescriptor.ImageHeight;
- BmHeader.biPlanes := 1; {Arcane and rarely used}
- BmHeader.biBitCount := 8; {Hmmm Should this be hardcoded ?}
- BmHeader.biCompression := BI_RGB; {Sorry Did not implement compression in this version}
- BmHeader.biSizeImage := 0; {Valid since we are not compressing the image}
- BmHeader.biXPelsPerMeter :=143; {Rarely used very arcane field}
- BmHeader.biYPelsPerMeter :=143; {Ditto}
- BmHeader.biClrUsed := 0; {all colors are used}
- BmHeader.biClrImportant := 0; {all colors are important}
- end;
- {------------------------------------------------------------------------------}
-
- {fills in Line list with current line}
- procedure TGif.CreateLine;
- var
- p: PBmLine;
- begin
- Application.ProcessMessages;
- {Create a new bmp line}
- New(p);
- {Fill in the data}
- p^.LineData := LineBuffer;
- p^.LineNo := CurrentY;
- {Add it to the list of lines}
- ImageLines.Add(p);
- {Prepare for the next line}
- Inc(CurrentY);
- if InterLaced then
- { Interlace support }
- begin
- case InterlacePass of
- 0: CurrentY := CurrentY + 7;
- 1: CurrentY := CurrentY + 7;
- 2: CurrentY := CurrentY + 3;
- 3: CurrentY := CurrentY + 1;
- end;
- if CurrentY >= ImageDescriptor.ImageHeight then
- begin
- Inc(InterLacePass);
- case InterLacePass of
- 1: CurrentY := 4;
- 2: CurrentY := 2;
- 3: CurrentY := 1;
- end;
- end;
- end;
- end;
- {------------------------------------------------------------------------------}
-
- procedure TGif.WriteBitmapToStream;
- var
- BitFile: TBitmapFileHeader;
- i: integer;
- Line: integer;
- ch: char;
- p: PBmLine;
- x: integer;
- begin
- BitFile.bfSize := (3*255) + {Color map info}
- sizeof(TBitmapFileHeader) +
- sizeof(TBitmapInfoHeader) +
- (ImageDescriptor.ImageHeight*ImageDescriptor.ImageWidth);
- BitFile.bfReserved1 := 0; {not currently used}
- BitFile.bfReserved2 := 0; {not currently used}
- BitFile.bfOffBits := (4*256)+
- sizeof(TBitmapFileHeader)+
- sizeof(TBitmapInfoHeader);
- {Write the file header}
- BmpStream.Clear;
- ch:='B';
- BmpStream.Write(ch,1);
- ch:='M';
- BmpStream.Write(ch,1);
- BmpStream.Write(BitFile.bfSize,sizeof(BitFile.bfSize));
- BmpStream.Write(BitFile.bfReserved1,sizeof(BitFile.bfReserved1));
- BmpStream.Write(BitFile.bfReserved2,sizeof(BitFile.bfReserved2));
- BmpStream.Write(BitFile.bfOffBits,sizeof(BitFile.bfOffBits));
- {Write the bitmap image header info}
- BmpStream.Write(BmHeader,sizeof(BmHeader));
- {Write the BGR palete inforamtion to this file}
- if UseLocalColors then {Use the local color table}
- begin
- for i:= 0 to 255 do
- begin
- BmpStream.Write(LocalColorTable[i].Blue,1);
- BmpStream.Write(LocalColorTable[i].Green,1);
- BmpStream.Write(LocalColorTable[i].Red,1);
- BmpStream.Write(ch,1); {Bogus palete entry required by windows}
- end;
- end
- else {Use the global table}
- begin
- for i:= 0 to 255 do
- begin
- BmpStream.Write(GlobalColorTable[i].Blue,1);
- BmpStream.Write(GlobalColorTable[i].Green,1);
- BmpStream.Write(GlobalColorTable[i].Red,1);
- BmpStream.Write(ch,1); {Bogus palete entry required by windows}
- end;
- end;
- {Init the Line Counter}
- Line := ImageDescriptor.ImageHeight;
- {Write out File lines in reverse order}
- while Line >= 0 do
- begin
- {Go through the line list in reverse order looking for the
- current Line. Use reverse order since non interlaced gifs are
- stored top to bottom. Bmp file need to be written bottom to
- top}
- for i := (ImageLines.Count - 1) downto 0 do
- begin
- p := ImageLines.Items[i];
- if p^.LineNo = Line then
- begin
- x := ImageDescriptor.ImageWidth;
- BmpStream.Write(p^.LineData, x);
- ch := chr(0);
- while (x and 3) <> 0 do { Pad up to 4-byte boundary with zeroes }
- begin
- Inc(x);
- BmpStream.Write(ch, 1);
- end;
- break;
- end;
- end;
- Dec(Line);
- end;
- BmpStream.Seek(0,0); { reset mewmory stream}
- end;
-
- {------------------------------------------------------------------------------}
-
- procedure TGif.WriteBitmapToFile(ABMPName: string);
- begin
- WriteBitMapToStream;
- BmpStream.SaveToFile(ABMPName);
- end;
-
- {------------------------------------------------------------------------------}
-
- procedure Register;
- begin
- RegisterComponents('Samples',[TGifImage]);
- end;
- end.
-